package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.srb.core.enums.*;
import com.atguigu.srb.core.exception.Assert;
import com.atguigu.srb.core.exception.BusinessException;
import com.atguigu.srb.core.exception.UnifiedExceptionHandler;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.mapper.BorrowerMapper;
import com.atguigu.srb.core.mapper.LendItemMapper;
import com.atguigu.srb.core.mapper.UserInfoMapper;
import com.atguigu.srb.core.pojo.entity.*;
import com.atguigu.srb.core.mapper.LendMapper;
import com.atguigu.srb.core.pojo.vo.BorrowInfoApprovalVO;
import com.atguigu.srb.core.pojo.vo.BorrowerDetailVO;
import com.atguigu.srb.core.result.ResponseEnum;
import com.atguigu.srb.core.service.*;
import com.atguigu.srb.core.utils.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 标的准备表 服务实现类
 * </p>
 *
 * @author AtLong
 * @since 2022-06-11
 */
@Transactional(rollbackFor = Exception.class)
@Service
@Slf4j
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements LendService {
    @Resource
    private LendMapper lendMapper;
    @Resource
    private DictService dictService;
    @Resource
    private BorrowerService borrowerService;
    @Resource
    private UserBindService userBindService;
    @Resource
    private LendItemMapper lendItemMapper;
    @Resource
    private UserAccountService userAccountService;
    @Resource
    private TransFlowService transFlowService;
    @Resource
    private LendItemService lendItemService;
    @Resource
    private UserInfoMapper userInfoMapper;
    @Resource
    private LendReturnService lendReturnService;
    @Resource
    private LendItemReturnService lendItemReturnService;

    /**
     * 创建lend标的
     *
     * @param borrowInfoApprovalVO
     * @param borrowInfo
     */
    @Override
    public void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo) {
        Lend lend = new Lend();
        if (ObjectUtils.isEmpty(borrowInfoApprovalVO) || ObjectUtils.isEmpty(borrowInfo)) {
            throw new RuntimeException("标的创建失败,borrowInfoApprovalVO或者borrowInfo为空");
        }
        // 获取BorrowInfoApprovalVO表单数据
        String content = borrowInfoApprovalVO.getContent();
        String lendInfo = borrowInfoApprovalVO.getLendInfo();
        String lendStartDate = borrowInfoApprovalVO.getLendStartDate();
        BigDecimal lendYearRate = borrowInfoApprovalVO.getLendYearRate();
        BigDecimal serviceRate = borrowInfoApprovalVO.getServiceRate();
        String title = borrowInfoApprovalVO.getTitle();
        Long id = borrowInfoApprovalVO.getId();
        Integer borrowInfoApprovalVOStatus = borrowInfoApprovalVO.getStatus();

        lend.setUserId(borrowInfo.getUserId());
        lend.setBorrowInfoId(borrowInfo.getId());
        lend.setLendNo(LendNoUtils.getLendNo());
        lend.setTitle(title);
        lend.setAmount(borrowInfo.getAmount());
        lend.setPeriod(borrowInfo.getPeriod());
        lend.setLendYearRate(lendYearRate.divide(new BigDecimal(100)));
        lend.setServiceRate(serviceRate.divide(new BigDecimal(100)));

        lend.setReturnMethod(borrowInfo.getReturnMethod());
        lend.setLowestAmount(new BigDecimal(100));// 最低投资金额
        lend.setInvestAmount(new BigDecimal(0)); // 已经投资金额
        lend.setInvestNum(0); // 已经投入
        lend.setPublishDate(LocalDateTime.now());
        // 起息日期
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyy-MM-dd");
        LocalDate lendStartDate1 = LocalDate.parse(lendStartDate, dateTimeFormatter);
        lend.setLendStartDate(lendStartDate1);
        // 结束日期
        LocalDate lendEndDate = lendStartDate1.plusMonths(borrowInfo.getPeriod());
        lend.setLendEndDate(lendEndDate);
        // 标的描述
        lend.setLendInfo(lendInfo);
        // 平台预期收益 = 标的金额 * 年化 /12 *期数
        BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_CEILING);
        BigDecimal expectAmount = lend.getAmount().multiply(monthRate.multiply(new BigDecimal(lend.getPeriod())));
        lend.setExpectAmount(expectAmount);
        // 实际收益
        lend.setRealAmount(new BigDecimal(0));
        // 标的状态
        lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());
        // 审核时间
        lend.setCheckTime(LocalDateTime.now());
        // 审核人
        lend.setCheckAdminId(1L);
        // 放款时间

        log.info("创建生成标的lend---->>>" + lend.toString());
        // 插入数据
        lendMapper.insert(lend);

    }

    @Override
    public List<Lend> getLendList() {
        List<Lend> lendList = lendMapper.selectList(null);
        for (Lend lend : lendList) {
            String returnMethod = dictService.getNameByParentDictCodeAndValue(DictEnum.RETURN_METHOD.getDictCode(), lend.getReturnMethod());
            lend.getParam().put("returnMethod", returnMethod);
            lend.getParam().put("status", LendStatusEnum.getMsgByStatus(lend.getStatus()));
        }
        return lendList;
    }

    @Override
    public Map<String, Object> getLendDetail(Long lendId) {
        Map<String, Object> map = new HashMap<>();
        // 查询lend
        Lend lend = lendMapper.selectById(lendId);
        String returnMethod = dictService.getNameByParentDictCodeAndValue(DictEnum.RETURN_METHOD.getDictCode(), lend.getReturnMethod());
        lend.getParam().put("returnMethod", returnMethod);
        lend.getParam().put("status", LendStatusEnum.getMsgByStatus(lend.getStatus()));
        // 封装lend到map
        map.put("lend", lend);

        // 查询borrow
        QueryWrapper<Borrower> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", lend.getUserId());
        Borrower borrower = borrowerService.getOne(queryWrapper);
        BorrowerDetailVO borrowerDetailVO = borrowerService.getDetailByBorrowerId(borrower.getId());
        // 封装borrow到map
        map.put("borrower", borrowerDetailVO);
        // 返回数据
        return map;
    }

    /**
     * 计算投资收益
     * ONE(1, "等额本息"),
     * TWO(2, "等额本金"),
     * THREE(3, "每月还息一次还本"),
     * FOUR(4, "一次还本还息"),
     *
     * @param investAmount
     * @param totalMonth
     * @param returnMethod
     * @return
     */
    @Override
    public BigDecimal getInterestCount(BigDecimal investAmount, BigDecimal yearRate, Integer totalMonth, Integer returnMethod) {
        // 投资收益还款方式
        int oneMethod = ReturnMethodEnum.ONE.getMethod();// 等额本息
        Integer twoMethod = ReturnMethodEnum.TWO.getMethod();// 等额本息
        Integer threeMethod = ReturnMethodEnum.THREE.getMethod();// 等额本息
        Integer fourMethod = ReturnMethodEnum.FOUR.getMethod();// 等额本息
        // 开始计算收益
        int r = returnMethod;
        BigDecimal bigDecimalCount;
        switch (r) {
            case 1:
                bigDecimalCount = Amount1Helper.getInterestCount(investAmount, yearRate, totalMonth);
                break;
            case 2:
                bigDecimalCount = Amount2Helper.getInterestCount(investAmount, yearRate, totalMonth);
                break;
            case 3:
                bigDecimalCount = Amount3Helper.getInterestCount(investAmount, yearRate, totalMonth);
                break;
            case 4:
                bigDecimalCount = Amount4Helper.getInterestCount(investAmount, yearRate, totalMonth);
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + r);
        }
        return bigDecimalCount;
    }

    @Override
    public void updateLendInvestNumAndInvestAmount(String voteBindCode) {
        QueryWrapper<UserBind> userBindQueryWrapper = new QueryWrapper<>();
        userBindQueryWrapper.eq("bind_code", voteBindCode);
        UserBind voteUserBind = userBindService.getOne(userBindQueryWrapper);
        QueryWrapper<LendItem> lendItemQueryWrapper = new QueryWrapper<>();
        lendItemQueryWrapper.eq("invest_user_id", voteUserBind.getUserId());
        LendItem lendItem = lendItemMapper.selectOne(lendItemQueryWrapper);
        Long lendId = lendItem.getLendId();
        Lend lend = lendMapper.selectById(lendId);
        lend.setInvestNum(lend.getInvestNum() + 1);
        lend.setInvestAmount(lend.getInvestAmount().add(lendItem.getInvestAmount()));
        lendMapper.updateById(lend);
    }

    /**
     * 放款--->>>是一个同步接口，如果放款返回成功，那么我们要处理平台业务
     * （1）标的状态和标的平台收益
     * （2）给借款账号转入金额
     * （3）增加借款交易流水
     * （4）解冻并扣除投资人资金
     * （5）增加投资人交易流水
     * （6）生成借款人还款计划和出借人回款计划
     *
     * @param lendId
     * @return
     */
    @Override
    public void agreeAccountLend(Long lendId) {
        // 查询标的列表
        Lend lend = lendMapper.selectById(lendId);
        Assert.notEmpty(lend.getLendNo(), ResponseEnum.ERROR);
        // 封装参数
        Map<String, Object> map = new HashMap<>();
        map.put("agentId", HfbConst.AGENT_ID);
        map.put("agentProjectCode", lend.getLendNo()); // 标的编号
        String agentBillNo = LendNoUtils.getLoanNo(); // 放款编号
        map.put("agentBillNo", agentBillNo);
        // 平台实际收益 = 已经投金额 * 月年化 /12 *期数
        BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);
        BigDecimal realAmount = lend.getInvestAmount().multiply(monthRate).multiply(new BigDecimal(lend.getPeriod()));
        map.put("mchFee", realAmount);
        map.put("note", "放款了");
        map.put("timestamp", RequestHelper.getTimestamp());
        map.put("sign", RequestHelper.getSign(map));
        // 发送同步远程调用
        String url = HfbConst.MAKE_LOAD_URL; // 联调地址
        JSONObject jsonResult = RequestHelper.sendRequest(map, url);
        log.info("放款返回成功同步接口,如果放款参数---->" + jsonResult.toString());

        //放款失败  3.11.3.返回结果说明
        if (!"0000".equals(jsonResult.getString("resultCode"))) {
            throw new BusinessException(jsonResult.getString("resultMsg"));
        }

        //===== （1）标的状态和标的平台收益
        lend.setStatus(LendStatusEnum.PAY_RUN.getStatus());
        lend.setPaymentTime(LocalDateTime.now());
        lend.setRealAmount(realAmount);
        lendMapper.updateById(lend);
        //log.info("标的状态和标的平台收益--->>>" + LendStatusEnum.PAY_RUN.getStatus() + "\n ---->>>" + realAmount);

        //===== （2）给借款账号转入金额
        String voteAmt = (String) jsonResult.get("voteAmt"); // 放款金额
        Long borrowUserId = lend.getUserId();
        QueryWrapper<UserAccount> userAccountQueryWrapper = new QueryWrapper<>();
        userAccountQueryWrapper.eq("user_id", borrowUserId);
        UserAccount borrowUserAccount = userAccountService.getOne(userAccountQueryWrapper);
        borrowUserAccount.setAmount(borrowUserAccount.getAmount().add(new BigDecimal(voteAmt)));
        borrowUserAccount.setFreezeAmount(borrowUserAccount.getFreezeAmount().add(new BigDecimal(0)));
        userAccountService.saveOrUpdate(borrowUserAccount);


        //===== （3）增加借款人交易流水
        TransFlow borrowTransFlow = new TransFlow();
        borrowTransFlow.setUserId(borrowUserId);
        UserInfo borrowUserInfo = userInfoMapper.selectById(borrowUserId);
        borrowTransFlow.setUserName(borrowUserInfo.getName());
        borrowTransFlow.setTransNo(agentBillNo);
        borrowTransFlow.setTransType(TransTypeEnum.BORROW_BACK.getTransType());
        borrowTransFlow.setTransTypeName(TransTypeEnum.BORROW_BACK.getTransTypeName());
        borrowTransFlow.setTransAmount(new BigDecimal(voteAmt));
        borrowTransFlow.setMemo("借款放款到账，借款项目，编号：" + lend.getLendNo() + ", 项目名称:" + lend.getTitle());
        transFlowService.saveOrUpdate(borrowTransFlow);

        //===== （4）解冻并扣除投资人资金
        List<LendItem> lendItemList = lendItemService.selectLendItemListByLendIdWithStatus(lendId, 1);
        lendItemList.forEach(item -> {
            Long investUserId = item.getInvestUserId();
            QueryWrapper<UserAccount> userAccountQueryWrapper2 = new QueryWrapper<>();
            userAccountQueryWrapper2.eq("user_id", investUserId);
            UserAccount investUserAccount = userAccountService.getOne(userAccountQueryWrapper2);
            investUserAccount.setAmount(investUserAccount.getAmount().add(new BigDecimal("0")));
            BigDecimal investUserAccountFreezeAmount = investUserAccount.getFreezeAmount();
            investUserAccount.setFreezeAmount(investUserAccountFreezeAmount.add(investUserAccountFreezeAmount.negate()));
            userAccountService.saveOrUpdate(investUserAccount);

            //===== （5）增加投资人交易流水
            UserInfo InvestUserInfo = userInfoMapper.selectById(investUserId);
            TransFlow InvestTransFlow = new TransFlow();
            InvestTransFlow.setUserName(InvestUserInfo.getName());
            InvestTransFlow.setTransNo(LendNoUtils.getTransNo());
            InvestTransFlow.setTransType(TransTypeEnum.INVEST_UNLOCK.getTransType());
            InvestTransFlow.setTransTypeName(TransTypeEnum.INVEST_UNLOCK.getTransTypeName());
            InvestTransFlow.setTransAmount(investUserAccount.getAmount());
            InvestTransFlow.setMemo("冻结资金转出，出借放款，编号：" + lend.getLendNo() + ", 项目名称:" + lend.getTitle());
            transFlowService.saveOrUpdate(InvestTransFlow);

        });

        //===== （6）生成借款人还款计划和出借人回款计划
        // 放款成功生成借款人还款计划和投资人回款计划
        this.repaymentPlan(lend); // 有问题

    }

    /**
     * 还款计划
     *
     * @param lend
     */
    public void repaymentPlan(Lend lend) {
        // 还款计划列表
        List<LendReturn> lendReturnList = new ArrayList<>();

        // 按照还款期数生成还款计划
        Integer lendPeriod = lend.getPeriod();
        for (Integer i = 1; i <= lendPeriod; i++) {
            // 创建还款计划对象
            LendReturn lendReturn = new LendReturn();
            lendReturn.setLendId(lend.getId());
            lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
            lendReturn.setReturnNo(LendNoUtils.getReturnNo());
            lendReturn.setUserId(lend.getUserId());
            lendReturn.setAmount(lend.getAmount());
            lendReturn.setBaseAmount(lend.getInvestAmount()); // 计息本金额
            lendReturn.setCurrentPeriod(i);
            lendReturn.setLendYearRate(lend.getLendYearRate());
            lendReturn.setReturnMethod(lend.getReturnMethod());
            lendReturn.setFee(new BigDecimal(0));
            lendReturn.setReturnDate(lend.getLendStartDate().plusMonths(i)); // 第2个月开始还款
            lendReturn.setOverdue(false); // 是否逾期
            lendReturn.setOverdueTotal(new BigDecimal("0"));

            //说明：还款计划中的这三项 = 回款计划中对应的这三项和：因此需要先生成对应的回款计划
            //			lendReturn.setPrincipal();
            //			lendReturn.setInterest();
            //			lendReturn.setTotal();

            // 最后一个月, 标识为最后一次还款
            if (i.equals(lendPeriod)) {
                lendReturn.setLast(true);
            } else {
                lendReturn.setLast(false);
            }
            lendReturn.setStatus(0);
            // 将还款添加到集合
            lendReturnList.add(lendReturn);

        }
        // 批量保存
        lendReturnService.saveBatch(lendReturnList);

        //获取lendReturnList中还款期数与还款计划id对应map
        Map<Integer, Long> lendReturnMap = lendReturnList.stream().collect(
                Collectors.toMap(LendReturn::getCurrentPeriod, LendReturn::getId)
        );

        //======================================================
        //=============获取所有投资者，生成回款计划===================
        //======================================================
        //回款计划列表
        List<LendItemReturn> lendItemReturnAllList = new ArrayList<>();
        //获取投资成功的投资记录
        List<LendItem> lendItemList = lendItemService.selectLendItemListByLendIdWithStatus(lend.getId(), 1);
        for (LendItem lendItem : lendItemList) {
            //创建回款计划列表
            List<LendItemReturn> lendItemReturnList = this.returnInvest(lendItem.getId(), lendReturnMap, lend); // 有问题
            lendItemReturnAllList.addAll(lendItemReturnList);
        }

        //更新还款计划中的相关金额数据
        for (LendReturn lendReturn : lendReturnList) {

            BigDecimal sumPrincipal = lendItemReturnAllList.stream()
                    //过滤条件：当回款计划中的还款计划id == 当前还款计划id的时候
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    //将所有回款计划中计算的每月应收本金相加
                    .map(LendItemReturn::getPrincipal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal sumInterest = lendItemReturnAllList.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getInterest)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal sumTotal = lendItemReturnAllList.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getTotal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            lendReturn.setPrincipal(sumPrincipal); //每期还款本金
            lendReturn.setInterest(sumInterest); //每期还款利息
            lendReturn.setTotal(sumTotal); //每期还款本息
        }
        lendReturnService.updateBatchById(lendReturnList);
    }

    /**
     * 回款计划
     * @param lendItemId
     * @param lendReturnMap 还款期数与还款计划id对应map
     * @param lend
     * @return
     */
    public List<LendItemReturn> returnInvest(Long lendItemId, Map<Integer, Long> lendReturnMap, Lend lend) {
        // 投标信息
        LendItem lendItem = lendItemService.getById(lendItemId);

        // 投标金额 年化利率 投资期数
        BigDecimal investAmount = lendItem.getInvestAmount();
        BigDecimal lendYearRate = lendItem.getLendYearRate();
        Integer totalMonth = lend.getPeriod();

        // 根据还款方式计算本金和利息
        Map<Integer, BigDecimal> mapPrincipal = null;
        Map<Integer, BigDecimal> mapPInterest=null;
        // 开始计算收益
        Integer m = lend.getReturnMethod();
        switch (m) {
            case 1:
                mapPrincipal = Amount1Helper.getPerMonthPrincipal(investAmount, lendYearRate, totalMonth);
                mapPInterest = Amount1Helper.getPerMonthInterest(investAmount, lendYearRate, totalMonth);
                break;
            case 2:
                mapPrincipal = Amount2Helper.getPerMonthPrincipal(investAmount, lendYearRate, totalMonth);
                mapPInterest = Amount2Helper.getPerMonthInterest(investAmount, lendYearRate, totalMonth);
                break;
            case 3:
                mapPrincipal = Amount3Helper.getPerMonthPrincipal(investAmount, lendYearRate, totalMonth);
                mapPInterest = Amount3Helper.getPerMonthInterest(investAmount, lendYearRate, totalMonth);
                break;
            case 4:
                mapPrincipal = Amount4Helper.getPerMonthPrincipal(investAmount, lendYearRate, totalMonth);
                mapPInterest = Amount4Helper.getPerMonthInterest(investAmount, lendYearRate, totalMonth);
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + m);
        }

        // 创建回款计划列表
        List<LendItemReturn> lendItemReturnList = new ArrayList<>();

        // 根据还款期数获取还款计划的id
        for (Map.Entry<Integer, BigDecimal> entry : mapPrincipal.entrySet()) {
            Integer currentPeriod = entry.getKey();
            Long lendReturnId = lendReturnMap.get(currentPeriod);
            // 创建回款计划对象
            LendItemReturn lendItemReturn = new LendItemReturn();
            lendItemReturn.setLendReturnId(lendReturnId);
            lendItemReturn.setLendItemId(lendItemId);
            lendItemReturn.setLendId(lendItem.getLendId());
            lendItemReturn.setInvestUserId(lendItem.getInvestUserId());
            lendItemReturn.setInvestAmount(lendItem.getInvestAmount());
            lendItemReturn.setLendYearRate(lend.getLendYearRate());
            lendItemReturn.setReturnMethod(lend.getReturnMethod());
            lendItemReturn.setCurrentPeriod(currentPeriod);
            // 最后一次本金计算
            // 最后一期本金 = 本金 - 前几次之和
            //最后一次本金计算
            if (lendItemReturnList.size() > 0 && currentPeriod.intValue() == lend.getPeriod().intValue()) {
                //最后一期本金 = 本金 - 前几次之和
                //最后一期应还本金 = 用当前投资人的总投资金额 - 除了最后一期前面期数计算出来的所有的应还本金
                BigDecimal sumPrincipal = lendItemReturnList.stream()
                        .map(LendItemReturn::getPrincipal)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);

                BigDecimal lastPrincipal = lendItem.getInvestAmount().subtract(sumPrincipal);
                lendItemReturn.setPrincipal(lastPrincipal);
                // 利息
                BigDecimal sumInterest = lendItemReturnList.stream()
                        .map(LendItemReturn::getInterest)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);

                BigDecimal lastInterest = lendItem.getExpectAmount().subtract(sumInterest);
                lendItemReturn.setPrincipal(lastInterest);
            } else {
                lendItemReturn.setPrincipal(mapPrincipal.get(currentPeriod));
                lendItemReturn.setInterest(mapPInterest.get(currentPeriod));
            }
            lendItemReturn.setTotal(lendItemReturn.getPrincipal().add(lendItemReturn.getInterest()));   //===== 有问题 =====
            lendItemReturn.setFee(new BigDecimal("0"));
            lendItemReturn.setReturnDate(lend.getLendStartDate().plusMonths(currentPeriod));

            //是否逾期，默认未逾期
            lendItemReturn.setOverdue(false);
            lendItemReturn.setStatus(0);
            // 添加到集合中
            lendItemReturnList.add(lendItemReturn);
        }
        // 批量添加回款计划列表
        lendItemReturnService.saveBatch(lendItemReturnList);
        return lendItemReturnList;
    }
}
